using System;
using System.Linq.Expressions;
using System.Reflection;

namespace Fuse8.DomainFramework.Practices.SystemFacade.Extensions
{
	internal static class ExpressionExtensions
	{
		public static string MemberName<T, TProperty>(this Expression<Func<T, TProperty>> expression)
		{
			return expression.Body.MemberName();
		}

		public static MethodInfo MethodInfo (this Expression expression)
		{
			var methodExpression = expression as MethodCallExpression;
			return methodExpression.Method;
		}

		public static PropertyInfo PropertyInfo(this Expression expression)
		{
			var memberExpression = expression as MemberExpression;

			if (expression.NodeType == ExpressionType.Convert)
			{
				memberExpression = ((UnaryExpression)expression).Operand as MemberExpression;
			}
			else if (expression.NodeType == ExpressionType.MemberAccess)
			{
				memberExpression = expression as MemberExpression;
			}

			if (memberExpression == null)
			{
				throw new InvalidOperationException("Expression must be a member expression");
			}

			if (memberExpression.Member.MemberType != MemberTypes.Property)
			{
				throw new InvalidOperationException("Expression has to provide access to property");
			}

			return memberExpression.Member as PropertyInfo;
		}
		
		public static string MemberName(this Expression expression)
		{
			return PropertyInfo(expression).Name;
		}

		public static TAttribute GetAttribute<TAttribute, TModel, TProperty>(this Expression<Func<TModel, TProperty>> expression)
				where TAttribute : Attribute
		{
			var memberExpression = expression.Body as MemberExpression;
			if (memberExpression == null)
				throw new InvalidOperationException("Expression must be a member expression");

			var attributes = memberExpression.Member.GetCustomAttributes(typeof(TAttribute), true);
			return attributes.Length > 0 ? attributes[0] as TAttribute : null;
		}

	}
}